package demo.db.redis;

import java.io.Serializable;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import demo.java.lang.ThreadDemo;
import demo.java.util.concurrent.ThreadFactoryDemo;
import demo.vo.CachedObject;
import redis.clients.jedis.JedisShardInfo;

public class RedisTemplateDemo {

    private static final Logger logger = LoggerFactory.getLogger(RedisTemplateDemo.class);

    public static final Random random = new Random();

    public static final JedisConnectionFactory FACTORY = RedisTemplateUtils.buildJedisConnectionFactory("172.16.0.140",
            6379, 3, "sider");

    public static void main(String[] args) {
    }

    @Test
    public void rPush() {
        RedisTemplate<String, Object> template = RedisTemplateUtils
                .buildWithGenericJackson2JsonRedisSerializer(FACTORY);
        ListOperations<String, Object> listOperations = template.opsForList();
        String key = "rPush";
        Long count = listOperations.rightPush(key, System.currentTimeMillis());
        logger.info("rPush后返回长度：{}", count.toString());
        if (count == 1L) {
            template.expire(key, 30, TimeUnit.DAYS);
        }
    }

    @Test
    public void hIncrBy() {
        RedisTemplate<String, Object> template = RedisTemplateUtils
                .buildWithGenericJackson2JsonRedisSerializer(FACTORY);
        HashOperations<String, String, Object> hashOperations = template.opsForHash();
        String key = "hIncrBy";
        Long sum = hashOperations.increment(key, "test", 1L);
        logger.info("hIncrBy后返回和：{}", sum);
        if (sum == 1L) {
            template.expire(key, 30, TimeUnit.DAYS);
        }
    }

    @Test
    public void testJDK() {
        String key = "JDK";
        RedisTemplate<String, Serializable> template = RedisTemplateUtils.buildJDKObjectRedisTemplate(FACTORY);
        template.opsForValue().set(key, "Hello", 3, TimeUnit.MINUTES);

        String value = (String) template.opsForValue().get(key);

        logger.info("{}", value);
        FACTORY.destroy();
    }

    @Test
    public void testGenericJackson2Json() {
        String key = "GenericJackson2JsonRedisSerializer";
        RedisTemplate<String, Object> template = RedisTemplateUtils
                .buildWithGenericJackson2JsonRedisSerializer(FACTORY);
        template.opsForValue().set(key, new CachedObject("Hello"), 3, TimeUnit.MINUTES);

        CachedObject cache = (CachedObject) template.opsForValue().get(key);

        logger.info("{}", cache);
        FACTORY.destroy();
    }

    @Test
    public void testJackson2Json() {
        String key = "Jackson2JsonRedisSerializer";
        RedisTemplate<String, CachedObject> template = RedisTemplateUtils.buildWithJackson2JsonRedisSerializer(FACTORY);
        template.opsForValue().set(key, new CachedObject("Hello"), 3, TimeUnit.MINUTES);

        CachedObject cache = template.opsForValue().get(key);

        logger.info("{}", cache);
        FACTORY.destroy();
    }

    /**
     * 尝试用Redis实现一个分布式锁
     * <p>
     * Redis Setnx 命令:SETNX KEY_NAME VALUE //SET if Not eXists，设置成功，返回 1 。 设置失败，返回 0 。
     * 
     */
    static void demoRedisLock() {
        StringRedisTemplate template = RedisTemplateUtils.buildStringRedisTemplate(FACTORY);
        ThreadFactory threadFactory = new ThreadFactoryDemo.SpecialThreadFactory(
                new ThreadDemo.UncaughtExceptionHandlerDemo("线程未捕获的异常"));

        ThreadPoolExecutor executor = new ThreadPoolExecutor(50, 50, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(), threadFactory);

        String lockKey = "test:redis.lock";
        for (int i = 0; i < 50; i++) {

            executor.submit(() -> {
                while (true) {
                    // 获取锁
                    if (template.opsForValue().setIfAbsent(lockKey, "1")) {
                        logger.info("获取锁.");
                        ThreadDemo.safeSleep(random.nextInt(50));
                        logger.info("释放锁.");
                        template.delete(lockKey);
                        break;
                    } else {
                        ThreadDemo.safeSleep(random.nextInt(10));
                    }
                }
            });

        }
        executor.shutdown();
    }

    @Test
    public void demo1() {
        StringRedisTemplate template = RedisTemplateUtils.buildStringRedisTemplate(FACTORY);
        template.opsForValue().set("1", "1");
        RedisConnectionFactory connectionFactory = template.getConnectionFactory();
        RedisConnection redisConnection = connectionFactory.getConnection();
        String pingResult = redisConnection.ping();
        System.err.println(pingResult);
        String clientName = redisConnection.getClientName();
        System.err.println(clientName);
        JedisConnectionFactory jedisConnectionFactory = (JedisConnectionFactory) connectionFactory;
        String hostName = jedisConnectionFactory.getHostName();
        System.err.println(hostName);
        JedisShardInfo jediShardInfo = jedisConnectionFactory.getShardInfo();
        System.out.println(jediShardInfo);
    }

}
